/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * The software in this package is published under the terms of the GPL v2.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE-GPL.txt file.
 */

package org.cemantika.management;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.cemantika.contextconsumer.ContextConsumerAdapter;
import org.cemantika.contextsource.ContextSourceAdapter;
import org.cemantika.exception.ContextUserConfigurationException;
import org.cemantika.exception.KnowledgeSessionNotCreatedException;
import org.cemantika.infrastructure.model.ContextUser;
import org.cemantika.management.drools.DroolsConfiguration;
import org.cemantika.management.drools.DroolsContextualManipulation;
import org.cemantika.management.drools.IDroolsContextualManipulation;
import org.cemantika.metamodel.structure.ContextualElement;
import org.cemantika.metamodel.structure.ContextualEntity;
import org.cemantika.metamodel.structure.Focus;
import org.cemantika.model.ContextConsumerModel;
import org.cemantika.model.ContextSourceModel;
import org.cemantika.util.CemantikaLogger;
import org.cemantika.util.SpringUtil;

/**
 * ContextManager represents the management of the contextual acquisition and dissemination of the
 * Context-Sensitive System.
 * 
 * @author andersonfellipe
 */
@SuppressWarnings("unchecked")
public class ContextManager {

    /**
     * Represents the instance of the {@link ContextManager}.
     */
    private static final ContextManager instance = new ContextManager();
    /**
     * Represents the list of {@link ContextualManipulation} for context manipulation purposes.
     */
    private Map<Integer, ContextualManipulation> contextualManipulations;
    /**
     * Represents the ids of the contextual manipulation objects.
     */
    private Map<ContextUser, Integer> contextualManipulationIds;
    /**
     * Represents the container of the context sources registered.
     */
    private List<ContextSourceModel> contextSources;
    /**
     * Represents the container of the context consumers registered.
     */
    private List<ContextConsumerModel> contextConsumerModels;
    /**
     * Represents the container of all focus and the information if they are activated of not.
     */
    private Map<String, Boolean> focusActivationList;
    /**
     * Represents the cache of the contextual elements recently updated.
     */
    private LinkedList<String> contextualElementAcquisitionCache;

    /**
     * Creates a new instance of ContextManager. It is private because this class implements the
     * design pattern singleton.
     */
    private ContextManager() {
        contextSources = new LinkedList<ContextSourceModel>();
        contextConsumerModels = new LinkedList<ContextConsumerModel>();
        focusActivationList = new HashMap<String, Boolean>();
        contextualElementAcquisitionCache = new LinkedList<String>();
        contextualManipulations = new HashMap<Integer, ContextualManipulation>();
        contextualManipulationIds = new HashMap<ContextUser, Integer>();
    }

    /**
     * Represents the method to get the {@link ContextManager} instance.
     * 
     * @return the singleton instance of the {@link ContextManager}.
     */
    public static ContextManager getInstance() {
        return instance;
    }

    /**
     * Represents the get to obtain the id of the session related to this user.
     * 
     * @param contextUser the object of a {@link ContextUser}.
     * @return the id of the session related to this {@link ContextUser}.
     */
    private Integer getContextualManipulationIdByContextUser(ContextUser contextUser) {
        Integer id;
        if (contextUser == null) {
            id = 0; // the index of the default contextual manipulation object
        } else {
            id = contextualManipulationIds.get(contextUser);
        }
        return id;
    }
    
    /**
     * Represents the search of the {@link DroolsContextualManipulation} that can be related to this
     * focus.
     * 
     * @param focus the object of a {@link ContextConsumerAdapter}.
     * @return the id of the related {@link DroolsContextualManipulation} if exists, or null otherwise.
     */
    public DroolsContextualManipulation searchDroolsContextualManipulation(ContextConsumerAdapter focus) {
        Set<Integer> ids = contextualManipulations.keySet();
        for (Integer i : ids) {
            if (getDroolsContextualManipulation(i).getFacts().keySet().contains(focus)) {
                return getDroolsContextualManipulation(i);
            }
        }
        return null;
    }

    /**
     * Represents the search of the {@link ContextualManipulation} that can be related to this
     * focus.
     * 
     * @param focus the object of a {@link ContextConsumerAdapter}.
     * @return the id of the related {@link ContextualManipulation} if exists, or 0 otherwise.
     */
    private Integer searchContextualManipulationId(ContextConsumerAdapter focus) {
        Set<Integer> ids = contextualManipulations.keySet();
        for (Integer i : ids) {
            if (getDroolsContextualManipulation(i).getFacts().keySet().contains(focus)) {
                return getDroolsContextualManipulation(i).getId();
            }
        }
        return 0;
    }

    /**
     * Represents the test if there is a knowledge session within the
     * {@link ContextualManipulation}.
     * 
     * @return true if has a knowledge session.
     */
    private boolean hasKnowledgeSessionInContextualManipulation(ContextUser contextUser) {
        Integer id = getContextualManipulationIdByContextUser(contextUser);
        if (id == null) {
            return false;
        }
        return contextualManipulations.get(id).hasKnowledgeSession();
    }

    /**
     * Represents the configuration of a {@link ContextUser} related to a unique
     * {@link DroolsContextualManipulation} that contains the knowledge session.
     * 
     * @param contextUser the object of a {@link ContextUser}.
     */
    @SuppressWarnings("restriction")
    public void configureContextUser(ContextUser contextUser) throws ContextUserConfigurationException {
        if (!hasKnowledgeSessionInContextualManipulation(contextUser)) {
            if (!SpringUtil.isSpringConfigured()) {
                throw new ContextUserConfigurationException();
            }
            DroolsContextualManipulation contextualManipulation = DroolsConfiguration.configureContextUser(contextUser);
            contextualManipulations.put(contextualManipulation.getId(), contextualManipulation);
            contextualManipulationIds.put(contextUser, contextualManipulation.getId());
        } else {
            CemantikaLogger.logger.warn("** User '" + contextUser.toString() + "' already configured.");
        }
    }

    /**
     * Add a simple fact object into session.
     * 
     * @param factObject the object of the fact to add.
     * @throws KnowledgeSessionNotCreatedException
     */
    public void addFact(Object fact) {
        addFactToContextualManipulation(fact, null);
    }

    /**
     * Add a simple fact object into session.
     * 
     * @param factObject the object of the fact to add.
     * @param contextUser the object of a {@link ContextUser}.
     * @throws KnowledgeSessionNotCreatedException
     */
    public void addFact(Object fact, ContextUser contextUser) {
        addFactToContextualManipulation(fact, contextUser);
    }

    @SuppressWarnings("restriction")
    private void addFactToContextualManipulation(Object fact, ContextUser contextUser) {
        try {
            Integer id = getContextualManipulationIdByContextUser(contextUser);
            getDroolsContextualManipulation(id).addFact(fact);
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Remove a simple fact object into session.
     * 
     * @param factObject the object of the fact to remove.
     * @throws KnowledgeSessionNotCreatedException
     */
    public void removeFact(Object fact) {
        removeFactToContextualManipulation(fact, null);
    }

    /**
     * Remove a simple fact object into session.
     * 
     * @param factObject the object of the fact to remove.
     * @param contextUser the object of a {@link ContextUser}.
     * @throws KnowledgeSessionNotCreatedException
     */
    public void removeFact(Object fact, ContextUser contextUser) {
        removeFactToContextualManipulation(fact, contextUser);
    }

    @SuppressWarnings("restriction")
    private void removeFactToContextualManipulation(Object fact, ContextUser contextUser) {
        try {
            Integer id = getContextualManipulationIdByContextUser(contextUser);
            getDroolsContextualManipulation(id).removeFact(fact);
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Update a simple fact object into session.
     * 
     * @param factObject the object of the fact to update.
     * @throws KnowledgeSessionNotCreatedException
     */
    public void updateFact(Object fact) {
        updateFactToContextualManipulation(fact, null);
    }

    /**
     * Update a simple fact object into session.
     * 
     * @param factObject the object of the fact to update.
     * @param contextUser the object of a {@link ContextUser}.
     * @throws KnowledgeSessionNotCreatedException
     */
    public void updateFact(Object fact, ContextUser contextUser) {
        updateFactToContextualManipulation(fact, contextUser);
    }

    @SuppressWarnings("restriction")
    private void updateFactToContextualManipulation(Object fact, ContextUser contextUser) {
        try {
            Integer id = getContextualManipulationIdByContextUser(contextUser);
            getDroolsContextualManipulation(id).updateFact(fact);
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    /**
     * Remove all facts being manipulated by CEManTIKA with the default
     * {@link ContextualManipulation}.
     */
    public void removeAllFactsAndFoci() {
        removeAllFactsAndFociFromContextualManipulation(null);
    }

    /**
     * Remove all facts being manipulated by CEManTIKA passing the object associated to a
     * {@link ContextUser}.
     * 
     * @param contextUser the object of a {@link ContextUser}.
     */
    public void removeAllFactsAndFoci(ContextUser contextUser) {
        removeAllFactsAndFociFromContextualManipulation(contextUser);
    }

    @SuppressWarnings("restriction")
    private void removeAllFactsAndFociFromContextualManipulation(ContextUser contextUser) {
        try {
            Integer id = getContextualManipulationIdByContextUser(contextUser);
            getDroolsContextualManipulation(id).removeAllFactsAndFoci();
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Add the focus to the foci being manipulated by CEManTIKA with the default
     * {@link ContextualManipulation}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to add.
     */
    public void addFocus(ContextConsumerAdapter focus) {
        addFocusToContextualManipulation(focus, null);
    }

    /**
     * Add the focus to the foci being manipulated by CEManTIKA passing the object associated to a
     * {@link ContextUser}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to add.
     * @param contextUser the object of a {@link ContextUser}.
     */
    public void addFocus(ContextConsumerAdapter focus, ContextUser contextUser) {
        addFocusToContextualManipulation(focus, contextUser);
    }

    @SuppressWarnings("restriction")
    private void addFocusToContextualManipulation(ContextConsumerAdapter focus, ContextUser contextUser) {
        try {
            Integer id = getContextualManipulationIdByContextUser(contextUser);
            contextualManipulations.get(id).addFocus(focus);
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Remove the focus from the foci being manipulated by CEManTIKA with the default
     * {@link ContextualManipulation}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to remove.
     */
    public void removeFocus(ContextConsumerAdapter focus) {
        removeFocusFromContextualManipulation(focus, null);
    }

    /**
     * Remove the focus from the foci being manipulated by CEManTIKA passing the object associated
     * to a {@link ContextUser}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to remove.
     * @param contextUser the object of a {@link ContextUser}.
     */
    public void removeFocus(ContextConsumerAdapter focus, ContextUser contextUser) {
        removeFocusFromContextualManipulation(focus, contextUser);
    }

    @SuppressWarnings("restriction")
    private void removeFocusFromContextualManipulation(ContextConsumerAdapter focus, ContextUser contextUser) {
        try {
            Integer id = getContextualManipulationIdByContextUser(contextUser);
            contextualManipulations.get(id).removeFocus(focus);
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Remove all foci being manipulated by CEManTIKA with the default
     * {@link ContextualManipulation}.
     */
    public void removeAllFoci() {
        removeAllFociFromContextualManipulation(null);
    }

    /**
     * Remove all foci being manipulated by CEManTIKA passing the object associated to a
     * {@link ContextUser}.
     * 
     * @param contextUser the object of a {@link ContextUser}.
     */
    public void removeAllFoci(ContextUser contextUser) {
        removeAllFociFromContextualManipulation(contextUser);
    }

    private void removeAllFociFromContextualManipulation(ContextUser contextUser) {
        while (!contextConsumerModels.isEmpty()) {
            removeFocus(contextConsumerModels.get(0).getFocus(), contextUser);
        }
    }

    /**
     * Remove all foci being manipulated by CEManTIKA with the default
     * {@link ContextualManipulation}, where the name of its class is equals to the type parameter.
     * 
     * @param type a {@link String} representing the type.
     */
    public void removeAllFociOfType(String type) {
        removeAllFociOfTypeFromContextualManipulation(type, null);
    }

    /**
     * Remove all foci being manipulated by CEManTIKA passing the object associated to a
     * {@link ContextUser}, where the name of its class is equals to the type parameter.
     * 
     * @param type a {@link String} representing the type.
     * @param contextUser the object of a {@link ContextUser}.
     */
    public void removeAllFociOfType(String type, ContextUser contextUser) {
        removeAllFociOfTypeFromContextualManipulation(type, contextUser);
    }

    private void removeAllFociOfTypeFromContextualManipulation(String type, ContextUser contextUser) {
        List<ContextConsumerModel> contextConsumerList = new LinkedList<ContextConsumerModel>();
        contextConsumerList.addAll(contextConsumerModels);
        for (ContextConsumerModel contextConsumerModel : contextConsumerList) {
            if (contextConsumerModel.getFocus().getClass().getSimpleName().equals(type)) {
                removeFocus(contextConsumerModel.getFocus(), contextUser);
            }
        }
    }

    /**
     * Update the focus being manipulated by CEManTIKA with the default
     * {@link ContextualManipulation}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to update.
     */
    public void updateFocus(ContextConsumerAdapter focus) {
        updateFocusOfContextualManipulation(focus, null);
    }

    /**
     * Update the focus being manipulated by CEManTIKA passing the object associated to a
     * {@link ContextUser}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to update.
     * @param contextUser the object of a {@link ContextUser}.
     */
    public void updateFocus(ContextConsumerAdapter focus, ContextUser contextUser) {
        updateFocusOfContextualManipulation(focus, contextUser);
    }

    @SuppressWarnings("restriction")
    private void updateFocusOfContextualManipulation(ContextConsumerAdapter focus, ContextUser contextUser) {
        try {
            Integer id;
            // This code is needed when there is a contextual dissemination.
            if (contextualManipulations.get(1) != null && contextUser == null) {
                id = searchContextualManipulationId(focus);
            } else {
                id = getContextualManipulationIdByContextUser(contextUser);
            }
            contextualManipulations.get(id).updateFocus(focus);
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Represents the test if the {@link Focus} is registered in CEManTIKA with the default
     * {@link ContextualManipulation}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to test if is registered.
     * @return true if the focus is registered.
     */
    public boolean isFocusRegistered(ContextConsumerAdapter focus) {
        return isFocusRegisteredInContextualManipulation(focus, null);
    }

    /**
     * Represents the test if the {@link Focus} is registered in CEManTIKA passing the id of the
     * session associated to a {@link ContextUser}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to test if is registered.
     * @param contextUser the object of a {@link ContextUser}.
     * @return true if the focus is registered.
     */
    public boolean isFocusRegistered(ContextConsumerAdapter focus, ContextUser contextUser) {
        return isFocusRegisteredInContextualManipulation(focus, contextUser);
    }

    private boolean isFocusRegisteredInContextualManipulation(ContextConsumerAdapter focus, ContextUser contextUser) {
        Integer id = getContextualManipulationIdByContextUser(contextUser);
        return contextualManipulations.get(id).isFocusRegistered(focus);
    }

    /**
     * Represents the test if there is a knowledge session within the default
     * {@link ContextualManipulation}.
     * 
     * @return true if has a knowledge session.
     */
    public boolean hasKnowledgeSession() {
        return hasKnowledgeSessionInContextualManipulation(null);
    }
    
    /**
     * Represents the test if the {@link ContextConsumerAdapter} has a knowledge session passing
     * the id of the session associated to a {@link ContextUser}.
     * 
     * @param focus the {@link ContextConsumerAdapter} to test if is registered.
     * @param contextUser the object of a {@link ContextUser}.
     * @return true if the focus is registered.
     */
    public boolean hasKnowledgeSession(ContextUser contextUser) {
        return hasKnowledgeSessionInContextualManipulation(contextUser);
    }

    /**
     * Represents the registration of the context source responsible for the acquisition of a
     * contextual information.
     * 
     * @param contextSourceModel the object with information about the context source.
     */
    public void addContextSource(ContextSourceModel contextSourceModel) {
        contextSources.add(contextSourceModel);
    }

    /**
     * Represents the registration of a context consumer to be updated when the contextual element
     * change.
     * 
     * @param contextConsumerModel the object with information about the context consumer.
     */
    public void addContextConsumer(ContextConsumerModel contextConsumerModel) {
        contextConsumerModels.add(contextConsumerModel);
    }

    /**
     * Remove a {@link ContextConsumerModel} being manipulated by CEManTIKA.
     * 
     * @param contextConsumerModel the {@link ContextConsumerModel} to remove.
     */
    public void removeContextConsumer(ContextConsumerModel contextConsumerModel) {
        contextConsumerModels.remove(contextConsumerModel);
    }

    /**
     * Represents the registration of the activation of all focus within the Context-Sensitive
     * System.
     * 
     * @param key the class name of the focus.
     * @param isActivated the value if the focus is activated or not.
     */
    public void registerFocusActivation(String key, Boolean isActivated) {
        focusActivationList.put(key, isActivated);
    }

    /**
     * Represents the test if the focus is activated or not with the default
     * {@link ContextualManipulation}.
     * 
     * @param key the class name of the focus.
     * @return if it is activated or not. Return true if the focus activation is null, because can
     *         be a simple workflow execution.
     */
    public Boolean isFocusActivated(String key) {
        return isFocusActivatedInContextualManipulation(key, null);
    }

    /**
     * Represents the test if the focus is activated or not passing the object associated to a
     * {@link ContextUser}.
     * 
     * @param key the class name of the focus.
     * @param contextUser the object of a {@link ContextUser}.
     * @return if it is activated or not. Return true if the focus activation is null, because can
     *         be a simple workflow execution.
     */
    public Boolean isFocusActivated(String key, ContextUser contextUser) {
        return isFocusActivatedInContextualManipulation(key, contextUser);
    }

    private Boolean isFocusActivatedInContextualManipulation(String key, ContextUser contextUser) {
        Boolean hasFocusOfType = getDroolsContextualManipulation(contextUser).hasFocusOfType(key);
        Boolean focusActivation = focusActivationList.get(key);
        if (focusActivation != null && hasFocusOfType) {
            return focusActivation;
        }
        if (focusActivation == null) {
            return true;
        }
        return false;
    }

    /**
     * Represents the list of foci registered in the {@link ContextManager}.
     * 
     * @return all foci registered.
     */
    public Set<String> getFociRegistered() {
        return focusActivationList.keySet();
    }

    /**
     * Represents the addition of the element key representing a contextual element acquisition.
     * 
     * @param element the contextual element acquired.
     */
    public void addContextualElementAcquired(String element) {
        contextualElementAcquisitionCache.add(element);
    }

    /**
     * Represents the element key of the contextual element acquired.
     * 
     * @return the contextual element acquired.
     */
    public String getContextualElementAcquired() {
        if (!contextualElementAcquisitionCache.isEmpty()) {
            return contextualElementAcquisitionCache.removeFirst();
        } else {
            return null;
        }
    }

    /**
     * Represents the test if this {@link ContextualEntity} is related to this {@link Focus}.
     * 
     * @param contextConsumerModel the key to a list of contextual entities.
     * @param contextualEntity the contextual entity to verify if is related to this focus.
     * @return true if this contextual entity is equals to some contextual entity related to this
     *         focus.
     */
    public boolean isContextualEntityOfFocus(ContextConsumerModel contextConsumerModel, Object contextualEntity) {
        if (contextConsumerModels.contains(contextConsumerModel)) {
            int index = contextConsumerModels.indexOf(contextConsumerModel);
            if (contextConsumerModels.get(index).getContextualEntities().contains(contextualEntity)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns all {@link ContextConsumerModel} that are related to this element.
     * 
     * FIXME: this method has high computational cost to a high number of context consumers.
     * 
     * @param element the key of a {@link ContextualElement}.
     * @return a list of {@link ContextConsumerModel}.
     */
    public List<ContextConsumerModel> getContextConsumersOfElement(String element) {
        List<ContextConsumerModel> contextConsumersOfElement = new LinkedList<ContextConsumerModel>();
        for (ContextConsumerModel contextConsumerModel : contextConsumerModels) {
            if (contextConsumerModel.getElements().contains(element)) {
                contextConsumersOfElement.add(contextConsumerModel);
            }
        }
        return contextConsumersOfElement;
    }

    /**
     * Represents the get to obtain a {@link ContextSourceAdapter} configured with Spring.
     * 
     * @param contextSourceId the id of the bean that is a {@link ContextSourceAdapter}.
     * @return the object of the {@link ContextSourceAdapter}.
     */
    @SuppressWarnings("restriction")
    public ContextSourceAdapter getContextSourceAdapter(String contextSourceId) {
        return (ContextSourceAdapter) SpringUtil.getApplicationContext().getBean(contextSourceId);
    }

    /**
     * Represents the get to obtain a {@link ContextSourceAdapter} registered according to its
     * element and contextSourceType.
     * 
     * @param element the element this {@link ContextSourceAdapter} is responsible for.
     * @param contextSourceType the type of the context source related to this
     *        {@link ContextSourceAdapter}.
     * @return
     */
    public ContextSourceAdapter getContextSourceAdapter(String element, String contextSourceType) {
        for (ContextSourceModel contextSourceModel : contextSources) {
            if (contextSourceModel.getElement().equals(element)
                    && contextSourceModel.getContextSourceType().equals(contextSourceType)) {

                return contextSourceModel.getContextSourceAdapter();
            }
        }
        return null;
    }

    /**
     * Represents the clearing of all focus added to be managed by CEManTIKA with the default
     * {@link ContextualManipulation}.
     */
    public void clearContextualMemory() {
        clearContextualMemoryOfContextualManipulation(null);
    }

    /**
     * Represents the clearing of all focus added to be managed by CEManTIKA passing the object
     * associated to a {@link ContextUser}.
     * 
     * @param contextUser the object of a {@link ContextUser}.
     */
    public void clearContextualMemory(ContextUser contextUser) {
        clearContextualMemoryOfContextualManipulation(contextUser);
    }

    @SuppressWarnings("restriction")
    private void clearContextualMemoryOfContextualManipulation(ContextUser contextUser) {
        try {
            Integer id = getContextualManipulationIdByContextUser(contextUser);
            contextualManipulations.get(id).clearContextualMemory();
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Represents the halt of the inference engine with the default {@link ContextualManipulation}.
     */
    public void terminate() {
        terminateContextualManipulation(null);
    }

    /**
     * Represents the halt of the inference engine passing the object associated to a
     * {@link ContextUser}.
     */
    public void terminate(ContextUser contextUser) {
        terminateContextualManipulation(contextUser);
    }

    @SuppressWarnings("restriction")
    private void terminateContextualManipulation(ContextUser contextUser) {
        try {
            Integer id = getContextualManipulationIdByContextUser(contextUser);
            contextualManipulations.get(id).terminate();
        } catch (KnowledgeSessionNotCreatedException e) {
            CemantikaLogger.logger.error("** " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * Get the contextualManipulations value.
     * 
     * @return the contextualManipulations.
     */
    public Map<Integer, ContextualManipulation> getContextualManipulations() {
        return contextualManipulations;
    }

    /**
     * Assign the value of the field contextualManipulations.
     * 
     * @param contextualManipulations the contextualManipulations to set.
     */
    public void setContextualManipulations(Map<Integer, ContextualManipulation> contextualManipulations) {
        this.contextualManipulations = contextualManipulations;
    }

    /**
     * Represents the cast of the attribute contextualManipulations to a
     * {@link DroolsContextualManipulation} object to access methods defined into
     * {@link IDroolsContextualManipulation}.
     * 
     * @param contextUser the object of a {@link ContextUser}.
     * @return an object related to the interface {@link IDroolsContextualManipulation}.
     */
    public IDroolsContextualManipulation getDroolsContextualManipulation(ContextUser contextUser) {
        Integer index = contextualManipulationIds.get(contextUser);
        if (index == null) {
            return (IDroolsContextualManipulation) contextualManipulations.get(0);
        }
        return (IDroolsContextualManipulation) contextualManipulations.get(index);
    }

    /**
     * Represents the cast of the attribute contextualManipulations to a
     * {@link DroolsContextualManipulation} object to access methods defined into
     * {@link IDroolsContextualManipulation}.
     * 
     * @param id the id of the {@link DroolsContextualManipulation}.
     * @return an object related to the interface {@link IDroolsContextualManipulation}.
     */
    private DroolsContextualManipulation getDroolsContextualManipulation(Integer id) {
        return (DroolsContextualManipulation) contextualManipulations.get(id);
    }

    /**
     * Get the contextSources value.
     * 
     * @return the contextSources.
     */
    public List<ContextSourceModel> getContextSources() {
        return contextSources;
    }

    /**
     * Get the focusActivationList value.
     * 
     * @return the focusActivationList.
     */
    public Map<String, Boolean> getFocusActivationList() {
        return focusActivationList;
    }

}
